/**
  ******************************************************************************
  * @file    stm32_eval_spi_sd.c
  * @author  MCD Application Team
  * @version V4.5.1
  * @date    20-September-2021
  * @brief   This file provides a set of functions needed to manage the SPI SD
  *          Card memory mounted on STM32xx-EVAL board (refer to stm32_eval.h
  *          to know about the boards supporting this memory).
  *          It implements a high level communication layer for read and write
  *          from/to this memory. The needed STM32 hardware resources (SPI and
  *          GPIO) are defined in stm32xx_eval.h file, and the initialization is
  *          performed in SD_LowLevel_Init() function declared in stm32xx_eval.c
  *          file.
  *          You can easily tailor this driver to any other development board,
  *          by just adapting the defines for hardware resources and
  *          SD_LowLevel_Init() function.
  *
  *          +-------------------------------------------------------+
  *          |                     Pin assignment                    |
  *          +-------------------------+---------------+-------------+
  *          |  STM32 SPI Pins         |     SD        |    Pin      |
  *          +-------------------------+---------------+-------------+
  *          | SD_SPI_CS_PIN           |   ChipSelect  |    1        |
  *          | SD_SPI_MOSI_PIN / MOSI  |   DataIn      |    2        |
  *          |                         |   GND         |    3 (0 V)  |
  *          |                         |   VDD         |    4 (3.3 V)|
  *          | SD_SPI_SCK_PIN / SCLK   |   Clock       |    5        |
  *          |                         |   GND         |    6 (0 V)  |
  *          | SD_SPI_MISO_PIN / MISO  |   DataOut     |    7        |
  *          +-------------------------+---------------+-------------+
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2011 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "gd32f30x.h"
#include "stdint.h"
#include "string.h"
#include "DevicesDelay.h"
#include "DevicesSPI.h"

#include "stm32_eval_spi_sd.h"


#define SD_DUMMY_BYTE       0xFF

#define SD_CS_GPIO_PORT     GPIOB
#define SD_CS_PIN           GPIO_PIN_11

#define SD_CS_HIGH()        do{ GPIO_BOP(SD_CS_GPIO_PORT) = SD_CS_PIN; vDelayUs(1); }while(0)
#define SD_CS_LOW()         GPIO_BC(SD_CS_GPIO_PORT)  = SD_CS_PIN

#define SD_SPEED_LOW()      SPI1_SPEED_LOW()
#define SD_SPEED_HIGH()     SPI1_SPEED_HIGH()

#define SD_WriteByte        ucSPI1WriteReadByte
#define SD_ReadByte()       ucSPI1WriteReadByte(SD_DUMMY_BYTE)
#define SD_WriteBytes       cSPI1WriteDatas
#define SD_ReadBytes        cSPI1ReadDatas

SD_CardInfo SDCardInfo;

/**
  * @brief  Initializes the SD/SD communication.
  * @param  None
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_Init(void)
{
    uint8_t ucDummy[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

    /*!< SD chip select high */
    SD_CS_HIGH();

    rcu_periph_clock_enable(RCU_GPIOB);

    /* SPI0 GPIO config:NSS */
    gpio_init(SD_CS_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SD_CS_PIN);

//    /*!< Initialize SD_SPI */
//    vSPI1Init();

    SD_SPEED_LOW();

    /*!< Send dummy byte 0xFF, 10 times with CS high */
    /*!< Rise CS and MOSI for 80 clocks cycles */
    SD_WriteBytes(ucDummy, sizeof(ucDummy));

    /*------------Put SD in SPI mode--------------*/
    /*!< SD initialized and set to SPI mode properly */
    SD_GoIdleState();

    SD_TypeInit();

    SD_GetCardInfo(&SDCardInfo);

    SD_SPEED_HIGH();

    return 0;
}

/**
  * @brief  Returns information about specific card.
  * @param  cardinfo: pointer to a SD_CardInfo structure that contains all SD
  *         card information.
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo)
{
    SD_Error status;

    status  = SD_GetCSDRegister(&(cardinfo->SD_csd));
    status |= SD_GetCIDRegister(&(cardinfo->SD_cid));

    cardinfo->CardBlockSize = 1 << (cardinfo->SD_csd.RdBlockLen);
    cardinfo->CardCapacity  = (cardinfo->SD_csd.DeviceSize + 1) * cardinfo->CardBlockSize;

    /*!< Returns the reponse */
    return status;
}

/**
  * @brief  Send 5 bytes command to the SD card.
  * @param  Cmd: The user expected command to send to SD card.
  * @param  Arg: The command argument.
  * @param  Crc: The CRC.
  * @retval None
  */
void SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc)
{
    uint8_t Frame[6];

    Frame[0] = Cmd | 0x40;      /*!< Construct byte 1 */
    Frame[1] = Arg >> 24;       /*!< Construct byte 2 */
    Frame[2] = Arg >> 16;       /*!< Construct byte 3 */
    Frame[3] = Arg >> 8;        /*!< Construct byte 4 */
    Frame[4] = Arg;             /*!< Construct byte 5 */
    Frame[5] = Crc;             /*!< Construct CRC: byte 6 */

    /*!< Send the Cmd bytes */
    SD_WriteBytes(Frame, 6);
}

/**
  * @brief  Returns the SD response.
  * @param  None
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_GetResponse(uint8_t Response, int32_t iTimes)
{
    uint8_t ucValue;

    for(iTimes >>= 1; iTimes > 0; --iTimes)
    {
        /*!< Check if response is got or a timeout is happen */
        if((((ucValue = SD_ReadByte()) & 0x80) == 0) || (ucValue == Response))
        {
            break;
        }

        vDelayUs(2.0f);
    }

    return (ucValue == Response) ? SD_RESPONSE_NO_ERROR : SD_RESPONSE_FAILURE;
}

/**
  * @brief  Returns the SD status.
  * @param  None
  * @retval The SD status.
  */
uint16_t SD_GetStatus(void)
{
    uint16_t Status;

    SD_CS_LOW();

    /*!< Send CMD13 (SD_SEND_STATUS) to get SD status */
    SD_SendCmd(SD_CMD_SEND_STATUS, 0, 0xFF);

    Status  = SD_ReadByte();
    Status |= ((uint16_t)SD_ReadByte()) << 8;

    SD_CS_HIGH();

    return Status;
}

/**
  * @brief  Get SD card data response.
  * @param  None
  * @retval The SD status: Read data response xxx0<status>1
  *         - status 010: Data accecpted
  *         - status 101: Data rejected due to a crc error
  *         - status 110: Data rejected due to a Write error.
  *         - status 111: Data rejected due to other error.
  */
uint8_t SD_GetDataResponse(void)
{
    uint32_t i;
    uint8_t response, rvalue;

    for (i = 0; i <= 64; ++i)
    {
        /*!< Read resonse */
        response = SD_ReadByte() & 0x1F;

        switch (response)
        {
            case SD_DATA_OK:            rvalue = SD_DATA_OK;            break;

            case SD_DATA_CRC_ERROR:     return SD_DATA_CRC_ERROR;

            case SD_DATA_WRITE_ERROR:   return SD_DATA_WRITE_ERROR;

            default:                    rvalue = SD_DATA_OTHER_ERROR;   break;
        }

        /*!< Exit loop in case of data ok */
        if (rvalue == SD_DATA_OK)
            break;
    }

    /*!< Return response */
    return response;
}

/**
  * @brief  Put SD in Idle state.
  * @param  None
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_TypeInit(void)
{
    int32_t i;
    uint8_t response, bytes[4 + 1];

    SD_CS_LOW();

    /*!< Send CMD8：设置电压为2.7V-3.6V */
    SD_SendCmd(SD_CMD_SEND_IF_COND, 0x000001AAU, 0xFF);

    /*!< Wait for In Idle State Response (R1 Format) equal to 0x01 */
    SD_GetResponse(SD_IN_IDLE_STATE, 128);

    /*!< read R7 value */
    SD_ReadBytes(bytes, sizeof(bytes));

    SD_CS_HIGH();

    /* 重复发送CMD55 + ACMD41，直到SD卡准备就绪 */
    for(i = 0; i < 512; ++i)
    {
        /*!< SD chip select low */
        SD_CS_LOW();

        /*!< Send CMD55 */
        SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF);

        /*!< Wait for In Idle State Response (R1 Format) equal to 0x01 */
        SD_GetResponse(SD_IN_IDLE_STATE, 128);

        /*!< Send ACMD41 */
        SD_SendCmd(SD_APPCMD_SD_SEND_OP_COND, 0xC0100000U, 0xFF);

        /*!< Wait for In Idle State Response (R1 Format) equal to 0x00 */
        response = SD_GetResponse(SD_RESPONSE_NO_ERROR, 128);

        SD_CS_HIGH();

        vDelayUs(100.0f);

        if(response == SD_RESPONSE_NO_ERROR)
            break;
    }

    return response;
}

/**
  * @brief  Put SD in Idle state.
  * @param  None
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_GoIdleState(void)
{
    uint8_t response;

    SD_CS_LOW();

    /*!< Send CMD0 (SD_CMD_GO_IDLE_STATE) to put SD in SPI mode */
    SD_SendCmd(SD_CMD_GO_IDLE_STATE, 0, 0x95);

    /*!< Wait for In Idle State Response (R1 Format) equal to 0x01 */
    response = SD_GetResponse(SD_IN_IDLE_STATE, 512);

    SD_CS_HIGH();

    return response;
}

/**
  * @brief  Reads a block of data from the SD.
  * @param  pvDatas: pointer to the buffer that receives the data read from the
  *                  SD.
  * @param  uiBlockAddr: SD's internal address to read from.
  * @param  iBlockSize: the SD card Data block size.
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_ReadBlock(void* pvDatas, uint32_t uiBlockAddr, int32_t iBlockSize)
{
    SD_Error rvalue = SD_RESPONSE_FAILURE;

    SD_CS_LOW();

    /*!< Send CMD17 (SD_CMD_READ_SINGLE_BLOCK) to read one block */
    SD_SendCmd(SD_CMD_READ_SINGLE_BLOCK, uiBlockAddr, 0xFF);

    /*!< Check if the SD acknowledged the read block command: R1 response */
    if(SD_GetResponse(SD_RESPONSE_NO_ERROR, 128) == SD_RESPONSE_NO_ERROR)
    {
        /*!< Now look for the data token to signify the start of the data */
        if(SD_GetResponse(SD_START_DATA_SINGLE_BLOCK_READ, 4096) == SD_RESPONSE_NO_ERROR)
        {
            /*!< Read the SD block data : read NumByteToRead data */
            SD_ReadBytes(pvDatas, iBlockSize);
            /*!< Get CRC bytes (not really needed by us, but required by SD) */
            SD_ReadByte();
            SD_ReadByte();

            /*!< Set response value to success */
            rvalue = SD_RESPONSE_NO_ERROR;
        }
    }

    SD_CS_HIGH();

    /*!< Returns the reponse */
    return rvalue;
}

/**
  * @brief  Reads multiple block of data from the SD.
  * @param  pvDatas: pointer to the buffer that receives the data read from the
  *                  SD.
  * @param  uiBlockAddr: SD's internal address to read from.
  * @param  iBlockSize: the SD card Data block size.
  * @param  iBlockNumber: number of blocks to be read.
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_ReadMultiBlocks(void *pvDatas, uint32_t uiBlockAddr, int32_t iBlockSize, int32_t iBlockNumber)
{
    uint8_t *pcDatas = pvDatas;
    SD_Error rvalue = SD_RESPONSE_FAILURE;

    /*!< Data transfer */
    while((iBlockNumber--) > 0)
    {
        rvalue = SD_ReadBlock(pcDatas, uiBlockAddr, iBlockSize);
        /*!< Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */
        if (rvalue != SD_RESPONSE_NO_ERROR)
        {
            break;
        }

        pcDatas += iBlockSize;
        ++uiBlockAddr;
    }

    /*!< Returns the reponse */
    return rvalue;
}

/**
  * @brief  Writes a block on the SD
  * @param  pvDatas: pointer to the buffer containing the data to be written on
  *                  the SD.
  * @param  uiBlockAddr: address to write on.
  * @param  iBlockSize: the SD card Data block size.
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_WriteBlock(void* pvDatas, uint32_t uiBlockAddr, int32_t iBlockSize)
{
    int32_t i;
    SD_Error rvalue = SD_RESPONSE_FAILURE;

    SD_CS_LOW();

    /*!< Send CMD24 (SD_CMD_WRITE_SINGLE_BLOCK) to write multiple block */
    SD_SendCmd(SD_CMD_WRITE_SINGLE_BLOCK, uiBlockAddr, 0xFF);

    /*!< Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */
    if(SD_GetResponse(SD_RESPONSE_NO_ERROR, 128) == SD_RESPONSE_NO_ERROR)
    {
        /*!< Send the data token to signify the start of the data */
        SD_WriteByte(SD_START_DATA_SINGLE_BLOCK_WRITE);

        /*!< Write the block data to SD : write count data by block */
        SD_WriteBytes(pvDatas, iBlockSize);
        /*!< Put CRC bytes (not really needed by us, but required by SD) */
        SD_WriteByte(SD_DUMMY_BYTE);
        SD_WriteByte(SD_DUMMY_BYTE);

        /*!< Read data response */
        if (SD_GetDataResponse() == SD_DATA_OK)
        {
            rvalue = SD_RESPONSE_NO_ERROR;
        }
    }

    /*!< Wait null data */
    for(i = 0; (SD_ReadByte() == 0) && (i < 10000); ++i)
    {
        vDelayUs(5.0f);
    }

    SD_CS_HIGH();

    /*!< Returns the reponse */
    return rvalue;
}

/**
  * @brief  Writes many blocks on the SD
  * @param  pvDatas: pointer to the buffer containing the data to be written on
  *                  the SD.
  * @param  uiBlockAddr: address to write on.
  * @param  iBlockSize: the SD card Data block size.
  * @param  iBlockNumber: number of blocks to be written.
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_WriteMultiBlocks(const void* pvDatas, uint32_t uiBlockAddr, int32_t iBlockSize, int32_t iBlockNumber)
{
    uint8_t *pcDatas = (uint8_t *)pvDatas;
    SD_Error rvalue = SD_RESPONSE_FAILURE;

    /*!< Data transfer */
    while((iBlockNumber--) > 0)
    {
        rvalue = SD_WriteBlock(pcDatas, uiBlockAddr, iBlockSize);
        /*!< Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */
        if (rvalue != SD_RESPONSE_NO_ERROR)
        {
            break;
        }

        pcDatas += iBlockSize;
        ++uiBlockAddr;
    }

    /*!< Returns the reponse */
    return rvalue;
}

/**
  * @brief  Read the CSD card register.
  *         Reading the contents of the CSD register in SPI mode is a simple
  *         read-block transaction.
  * @param  SD_csd: pointer on an SCD register structure
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_GetCSDRegister(SD_CSD* SD_csd)
{
    SD_Error rvalue = SD_RESPONSE_FAILURE;
    uint8_t CSD_Tab[16 + 2];

    SD_CS_LOW();

    /*!< Send CMD9 (CSD register) or CMD10(CSD register) */
    SD_SendCmd(SD_CMD_SEND_CSD, 0, 0xFF);

    /*!< Wait for response in the R1 format (0x00 is no errors) */
    if(SD_GetResponse(SD_RESPONSE_NO_ERROR, 128) == SD_RESPONSE_NO_ERROR)
    {
        if(SD_GetResponse(SD_START_DATA_SINGLE_BLOCK_READ, 512) == SD_RESPONSE_NO_ERROR)
        {
            /*!< Store CSD register value on CSD_Tab */
            /*!< Get CRC 2byte (not really needed by us, but required by SD) */
            SD_ReadBytes(CSD_Tab, sizeof(CSD_Tab));

            /*!< Set response value to success */
            rvalue = SD_RESPONSE_NO_ERROR;
        }
    }

    SD_CS_HIGH();

    /*!< Byte 0 */
    SD_csd->CSDStruct       = (CSD_Tab[0] >> 6) & 3;
    SD_csd->Reserved1       = CSD_Tab[0] & 0x3F;

    /*!< Byte 1 */
    SD_csd->TAAC            = CSD_Tab[1];

    /*!< Byte 2 */
    SD_csd->NSAC            = CSD_Tab[2];

    /*!< Byte 3 */
    SD_csd->MaxBusClkFrec   = CSD_Tab[3];

    /*!< Byte 4 */
    SD_csd->CardComdClasses = ((uint16_t)CSD_Tab[4] << 4) | (CSD_Tab[5] >> 4);

    /*!< Byte 5 */
    SD_csd->RdBlockLen      = CSD_Tab[5] & 0x0F;

    /*!< Byte 6 */
    SD_csd->PartBlockRead   = (CSD_Tab[6] >> 7) & 1;
    SD_csd->WrBlockMisalign = (CSD_Tab[6] >> 6) & 1;
    SD_csd->RdBlockMisalign = (CSD_Tab[6] >> 5) & 1;
    SD_csd->DSRImpl         = (CSD_Tab[6] >> 4) & 1;

    SD_csd->Reserved2           = ((CSD_Tab[6] & 0x0F) << 2) | (CSD_Tab[7] >> 6);

    /*!< Byte 7、8、9 */
    SD_csd->DeviceSize          = (((uint16_t)CSD_Tab[7] & 0x3F) << 16) | ((uint16_t)CSD_Tab[8] << 8) | CSD_Tab[9];

    /*!< Byte 10 */
    SD_csd->DeviceSizeMul       = (CSD_Tab[10] >> 7) & 1;
    SD_csd->EraseSingEnable     = (CSD_Tab[10] >> 6) & 1;

    SD_csd->EraseGrSize         = ((CSD_Tab[10] & 0x3F) << 1) | (CSD_Tab[11] >> 7);

    /*!< Byte 11 */
    SD_csd->WrProtectGrSize     = (CSD_Tab[11] & 0x7F);

    /*!< Byte 12 */
    SD_csd->WrProtectGrEnable   = (CSD_Tab[12] >> 7) & 1;
    SD_csd->ManDeflECC          = (CSD_Tab[12] >> 5) & 3;
    SD_csd->WrSpeedFact         = (CSD_Tab[12] >> 2) & 7;
    SD_csd->MaxWrBlockLen       = ((CSD_Tab[12] & 0x03) << 2) | (CSD_Tab[13] >> 6);

    /*!< Byte 13 */
    SD_csd->WriteBlockPaPartial = (CSD_Tab[13] >> 5) & 1;
    SD_csd->ContentProtectAppli = CSD_Tab[13] & 0x1F;

    /*!< Byte 14 */
    SD_csd->FileFormatGrouop    = (CSD_Tab[14] >> 7) & 1;
    SD_csd->CopyFlag            = (CSD_Tab[14] >> 6) & 1;
    SD_csd->PermWrProtect       = (CSD_Tab[14] >> 5) & 1;
    SD_csd->TempWrProtect       = (CSD_Tab[14] >> 4) & 1;
    SD_csd->FileFormat          = (CSD_Tab[14] >> 2) & 3;
    SD_csd->ECC                 = CSD_Tab[14] & 3;

    /*!< Byte 15 */
    SD_csd->CSD_CRC             = CSD_Tab[15] >> 1;
    SD_csd->Reserved6           = CSD_Tab[15] & 1;

    /*!< Return the reponse */
    return rvalue;
}

/**
  * @brief  Read the CID card register.
  *         Reading the contents of the CID register in SPI mode is a simple
  *         read-block transaction.
  * @param  SD_cid: pointer on an CID register structure
  * @retval The SD Response:
  *         - SD_RESPONSE_FAILURE: Sequence failed
  *         - SD_RESPONSE_NO_ERROR: Sequence succeed
  */
SD_Error SD_GetCIDRegister(SD_CID* SD_cid)
{
    SD_Error rvalue = SD_RESPONSE_FAILURE;
    uint8_t CID_Tab[16 + 2];

    SD_CS_LOW();

    /*!< Send CMD10 (CID register) */
    SD_SendCmd(SD_CMD_SEND_CID, 0, 0xFF);

    /*!< Wait for response in the R1 format (0x00 is no errors) */
    if (SD_GetResponse(SD_RESPONSE_NO_ERROR, 128) == SD_RESPONSE_NO_ERROR)
    {
        if (SD_GetResponse(SD_START_DATA_SINGLE_BLOCK_READ, 512) == SD_RESPONSE_NO_ERROR)
        {
            /*!< Store CID register value on CID_Tab */
            /*!< Get CRC 2byte (not really needed by us, but required by SD) */
            SD_ReadBytes(CID_Tab, sizeof(CID_Tab));

            /*!< Set response value to success */
            rvalue = SD_RESPONSE_NO_ERROR;
        }
    }

    SD_CS_HIGH();

    /*!< Byte 0 */
    SD_cid->ManufacturerID  = CID_Tab[0];

    /*!< Byte 1、2 */
    SD_cid->OEM_AppliID     = ((uint32_t)CID_Tab[1] << 8) | CID_Tab[2];

    /*!< Byte 3、4、5、6 */
    SD_cid->ProdNameH       = ((uint32_t)CID_Tab[3] << 24) | ((uint32_t)CID_Tab[4] << 16) | ((uint32_t)CID_Tab[5] << 8) | CID_Tab[6];

    /*!< Byte 7 */
    SD_cid->ProdNameL       = CID_Tab[7];

    /*!< Byte 8 */
    SD_cid->ProdRev         = CID_Tab[8];

    /*!< Byte 9、10、11、12 */
    SD_cid->ProdSN          = ((uint32_t)CID_Tab[9] << 24) | ((uint32_t)CID_Tab[10] << 16) | ((uint32_t)CID_Tab[11] << 8) | CID_Tab[12];

    /*!< Byte 13、14 */
    SD_cid->Reserved1       = CID_Tab[13] >> 4;
    SD_cid->ManufactDate    = (((uint32_t)CID_Tab[13] & 0x0F) << 8) | CID_Tab[14];

    /*!< Byte 15 */
    SD_cid->CID_CRC         = CID_Tab[15] >> 1;
    SD_cid->Reserved2       = CID_Tab[15] & 1;

    /*!< Return the reponse */
    return rvalue;
}
